﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the Microsoft Public License (Ms-PL).
// See accompanying file License.txt or copy at http://mews.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Mews.Application
{
    using System;
    using System.Globalization;
    using System.Security.Cryptography;
    using System.Windows.Forms;

    using Mews.Application.Properties;
    using Mews.Net.Nntp.Cache;

    /// <summary>The options form of the application.</summary>
    internal sealed partial class OptionsForm : DoneCancelForm
    {
        private readonly Cache cache;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal OptionsForm(Form owner, Cache cache) : base(owner, Resources.Options, false, false, true)
        {
            this.cache = cache;
            this.InitializeComponent();

            this.groupsTreeView.ImageList =
                (this.CurrentAutoScaleDimensions.Height >= 192) ? this.node32ImageList : this.node16ImageList;
            this.UpdateGroupsTreeView();
            this.tabControl.SendToBack();
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        ////////// this.groupsTreeView handlers //////////

        private void OnGroupsTreeView_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            e.Cancel = true;
        }

        private void OnGroupsTreeView_NodeTap(object sender, EventArgs e)
        {
            this.ShowNodeForm();
        }

        private void OnGroupsTreeView_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                e.Handled = true;
                this.ShowNodeForm();
            }
        }

        ////////// this.ContextMenu handlers //////////

        private void OnContextMenu_Popup(object sender, EventArgs e)
        {
            var menuItems = this.contextMenu.MenuItems;
            menuItems.Clear();
            var selectedNodeTag = this.GetSelectedNodeTag<object>();

            if (selectedNodeTag is Cache)
            {
                menuItems.Add(this.addHostMenuItem);
            }
            else if (selectedNodeTag is IHost)
            {
                menuItems.Add(this.addGroupMenuItem);
                menuItems.Add(this.deleteHostMenuItem);
            }
            else
            {
                menuItems.Add(this.deleteGroupMenuItem);
            }
        }

        private void OnAddHostMenuItem_Click(object sender, EventArgs e)
        {
            using (var hostForm = new HostForm(
                this,
                this.cache.HostNameMaxLength,
                this.cache.UserNameMaxLength,
                this.cache.EmailAddressMaxLength,
                this.cache.AccountNameMaxLength,
                this.cache.PasswordMaxLength))
            {
                if (hostForm.ShowDialog() == DialogResult.OK)
                {
                    IHost host;

                    try
                    {
                        host = this.cache.AddHost(
                            hostForm.HostName,
                            hostForm.Port,
                            hostForm.UserName,
                            hostForm.EmailAddress,
                            hostForm.AccountName,
                            hostForm.Password);
                    }
                    catch (ArgumentException)
                    {
                        MessageBoxHelper.ShowError(Resources.HostAlreadyExistsText, Resources.HostAlreadyExistsCaption);
                        return;
                    }

                    AddHostNode(this.groupsTreeView.SelectedNode, host);
                }
            }
        }

        private void OnDeleteHostMenuItem_Click(object sender, EventArgs e)
        {
            this.DeleteSelectedNode(
                this.GetSelectedNodeTag<IHost>().Delete, Resources.DeleteHostCaption, Resources.DeleteHostText);
        }

        private void OnAddGroupMenuItem_Click(object sender, EventArgs e)
        {
            var host = this.GetSelectedNodeTag<IHost>();

            using (var groupForm = new GroupForm(
                this, host, this.cache.GroupNameMaxLength, this.cache.GroupDisplayNameMaxLength))
            {
                if (groupForm.ShowDialog() == DialogResult.OK)
                {
                    IGroup group;

                    try
                    {
                        group = host.AddGroup(
                            groupForm.GroupName,
                            groupForm.GroupDisplayName,
                            groupForm.NewInboundArticlesReceiveLimit,
                            groupForm.CachedInboundArticlesLimit);
                    }
                    catch (ArgumentException)
                    {
                        MessageBoxHelper.ShowError(
                            Resources.GroupAlreadyExistsText, Resources.GroupAlreadyExistsCaption);
                        return;
                    }

                    AddGroupNode(this.groupsTreeView.SelectedNode, group);
                }
            }
        }

        private void OnDeleteGroupMenuItem_Click(object sender, EventArgs e)
        {
            this.DeleteSelectedNode(
                this.GetSelectedNodeTag<IGroup>().Delete, Resources.DeleteGroupCaption, Resources.DeleteGroupText);
        }

        ////////// Helper methods //////////

        private void UpdateGroupsTreeView()
        {
            this.groupsTreeView.Nodes.Clear();
            TreeNode internetNode = new TreeNode("Internet");
            internetNode.ImageIndex = 0;
            internetNode.SelectedImageIndex = 0;
            internetNode.Tag = this.cache;
            this.groupsTreeView.Nodes.Add(internetNode);

            foreach (var host in this.cache.GetHosts())
            {
                TreeNode hostNode = AddHostNode(internetNode, host);

                foreach (var group in host.GetGroups())
                {
                    AddGroupNode(hostNode, group);
                }
            }

            this.groupsTreeView.SelectedNode = internetNode;
            this.groupsTreeView.Focus();
        }

        private void ShowNodeForm()
        {
            var host = this.GetSelectedNodeTag<IHost>();

            if (host != null)
            {
                string accountName = null;
                string password = null;

                try
                {
                    accountName = host.AccountName;
                    password = host.Password;
                }
                catch (CryptographicException)
                {
                    MessageBoxHelper.ShowError(
                        string.Format(CultureInfo.InvariantCulture, Resources.DecryptionFailed, host.Name),
                        Resources.DecryptionFailedCaption);
                }

                using (var hostForm = new HostForm(
                    this,
                    host.Name,
                    this.cache.HostNameMaxLength,
                    host.Port,
                    host.UserName,
                    this.cache.UserNameMaxLength,
                    host.EmailAddress,
                    this.cache.EmailAddressMaxLength,
                    accountName,
                    this.cache.AccountNameMaxLength,
                    password,
                    this.cache.PasswordMaxLength))
                {
                    if (hostForm.ShowDialog() == DialogResult.OK)
                    {
                        host.UserName = hostForm.UserName;
                        host.EmailAddress = hostForm.EmailAddress;
                        host.AccountName = hostForm.AccountName;
                        host.Password = hostForm.Password;
                    }
                }
            }
            else
            {
                var group = this.GetSelectedNodeTag<IGroup>();

                if (group != null)
                {
                    using (var groupForm = new GroupForm(
                        this,
                        group.Name,
                        this.cache.GroupNameMaxLength,
                        group.DisplayName,
                        this.cache.GroupDisplayNameMaxLength,
                        group.NewInboundArticlesReceiveLimit,
                        group.CachedInboundArticlesLimit))
                    {
                        if (groupForm.ShowDialog() == DialogResult.OK)
                        {
                            this.groupsTreeView.SelectedNode.Text = groupForm.GroupDisplayName;

                            // From a DB point of view the following procedure is questionable as the setting of each
                            // property leads to the immediate update of the corresponding value in the database. Of
                            // course, this should either be done in a transaction or in a single update. However, both
                            // would require a more complex IGroup interface, which does not seem to be worth the
                            // effort.
                            group.DisplayName = groupForm.GroupDisplayName;

                            if (groupForm.CachedInboundArticlesLimit >= group.CachedInboundArticlesLimit)
                            {
                                group.CachedInboundArticlesLimit = groupForm.CachedInboundArticlesLimit;
                                group.NewInboundArticlesReceiveLimit = groupForm.NewInboundArticlesReceiveLimit;
                            }
                            else
                            {
                                group.NewInboundArticlesReceiveLimit = groupForm.NewInboundArticlesReceiveLimit;
                                group.CachedInboundArticlesLimit = groupForm.CachedInboundArticlesLimit;
                            }
                        }
                    }
                }
            }
        }

        private T GetSelectedNodeTag<T>() where T : class
        {
            return this.groupsTreeView.SelectedNode.Tag as T;
        }

        private static TreeNode AddHostNode(TreeNode internetNode, IHost host)
        {
            TreeNode hostNode = new TreeNode(host.Name);
            hostNode.ImageIndex = 1;
            hostNode.SelectedImageIndex = 1;
            hostNode.Tag = host;
            internetNode.Nodes.Add(hostNode);
            internetNode.Expand();
            return hostNode;
        }

        private static void AddGroupNode(TreeNode hostNode, IGroup group)
        {
            TreeNode groupNode = new TreeNode(group.DisplayName);
            groupNode.ImageIndex = 2;
            groupNode.SelectedImageIndex = 2;
            groupNode.Tag = group;
            hostNode.Nodes.Add(groupNode);
            hostNode.Expand();
        }

        private void DeleteSelectedNode(Action deleteAction, string caption, string text)
        {
            if (MessageBoxHelper.ShowYesNo(text, caption) == DialogResult.No)
            {
                return;
            }

            Cursor.Current = Cursors.WaitCursor;

            try
            {
                try
                {
                    deleteAction();
                }
                catch (InvalidOperationException)
                {
                    MessageBoxHelper.ShowError(Resources.DeleteNotPermittedText, Resources.DeleteNotPermittedCaption);
                    return;
                }

                this.groupsTreeView.SelectedNode.Remove();
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }
    }
}